[JVM] Dalvik配置与JVM


内容有点乱,有空再整理一下

JVM内存

在了解dalvik之前,需要认识一下JVM,我整理了一张图:

dalvik

在dalvik中,GC的类型有三种:

1、GC_EXPLICIT:

​ 应用主动调用System.gc()产生的GC事件。

2、GC_FOR_ALLOC:

​ 内存分配时,发现可用内存不够时触发的GC事件。

3、GC_CONCURRENT:

给Java层的class分配内存后,计算已分配的大小达到阈值(当前DVM heap size小一点)时会触发的GC事件。

因为第2和第3中GC是由系统触发的,所以应用是无法减少这两种类型的GC事件。但需要减少这两种GC事件是,可以通过配置dalvik的系统属性或者修改dalvik的GC算法来实现,本文只对修改dalvik的系统属性的方式进行介绍。

dalvik与GC相关的属性有:

dalvik.vm.heapstartsize:初始化dalvik分配的内存大小。

dalvik.vm.heapsize:在mainfest中设置android:largeheap=”true”时,应用的最大内存,超过这个值会有OOM产生。

dalvik.vm.heaputilization、dalvik.vm.heapminfree 、dalvik.vm.heapmaxfree:dalvik GC时使用的参数。

设备列出的dalvik属性

dalvik.vm.heapstartsize=14m
dalvik.vm.heapgrowthlimit=192m
dalvik.vm.heapsize=512m
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapminfree=6m
dalvik.vm.heapmaxfree=8m

dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=96m
dalvik.vm.heapsize=256m
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapminfree=2m
dalvik.vm.heapmaxfree=8m

dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=128m
dalvik.vm.heapsize=512m
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapminfree=2m
dalvik.vm.heapmaxfree=8m

512m gives enough for the rest of the system and 128m splits 512m in half giving the java heap zize enough for apps to load fast and run smoothly, ive tested this on a few devices and each one passed.

超级急速流畅型: 
dalvik.vm.startheapsize=16m 
dalvik.vm.heapsize=48m 
dalvik.vm.execution-mode=int:jit 
dalvik.vm.dexopt-flags=v=n,o=v 
dalvik.vm.checkjni=false 

常用稳定加流畅型: 
dalvik.vm.startheapsize=8m 
dalvik.vm.heapsize=40m 
dalvik.vm.execution-mode=int:fast 
dalvik.vm.dexopt-flags=m=y 
dalvik.vm.checkjni=false 

超级稳定大内存型: 
dalvik.vm.startheapsize=4m 
dalvik.vm.heapsize=30m 
dalvik.vm.execution-mode=int:portable 
dalvik.vm.dexopt-flags=v=a,o=v dalvik.vm.verify-bytecode=true 
dalvik.vm.checkjni=true

Dalvik虚拟机 Dalvik虚拟机是Android操作系统的核心,是一切应用程序的基础。

所有程序在运行时均有Dalvik虚拟机对其进行解析和执行。

以下参数参加源码:platform/dalvik/kitkat-release/./vm/alloc/HeapSource.cpp

  • dalvik.vm.heapstartsize

    本参数控制Dalvik虚拟机在启动一个应用程序之后为其分配的初始堆栈大小,可填写的值为2m~48m。这里分配的内存容量会影响到整个系统对RAM的使用程度,和第一次使用应用程序时的流畅程序。这个值越大,系统消耗RAM则越快,但是应用程序打开后的反应也越快。值越小,系统的RAM剩余则越多,但是程序在启动后会很卡。它被转化为-Xms的选项。

    parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");
    
  • dalvik.vm.heapsize

    本参数控制单个Dalvik虚拟机可以分配到的最大堆栈量。这里分配的内存容量会影响到整个系统对RAM的使用程序,和程序在运行一段时间后的反应速度。这个值越大,系统消耗RAM则越快,但是程序会运行的非常稳定,尤其是游戏和视频程序的内容加载速度可以大幅度提升。值越小,系统的RAM剩余则越多,但是程序会很卡,尤其是游戏在切换场景Loading的时候会花费很多的时间。若应用程序需要使用超过这个值的内存时,将会触发系统的垃圾收集器,系统和程序就会卡顿。它被转化为-Xmx选项。

    parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
    
  • dalvik.vm.heapgrowthlimit

    它指定标准app的最大堆栈,如果在mainfest中设置Android:largeheap=”true”时,应用会使用最大内存,超过这个值会有 OOM产生。它被转化为-XX:HeapGrowthLimit选项。

    parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
    
  • dalvik.vm.heaptargetutilization

    它为VM提供了关于应该允许托管堆满的程度的提示。它被转化为-XX:HeapTargetUtilization选项。

    parseRuntimeOption("dalvik.vm.heaptargetutilization",
                             heaptargetutilizationOptsBuf,
                             "-XX:HeapTargetUtilization=");
    
  • dalvik.vm.heapminfree & dalvik.vm.heapmaxfree

    它们分别被转化为-XX:HeapMinFree-XX:HeapMaxFree。它们与-XX:HeapTargetUtilization结合使用,以确定堆需要增长时堆的增长量。

    parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");
    parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
    
  • dalvik.vm.lockprof.threshold: 本参数控制Dalvik虚拟机调试记录程序内部锁资源争夺的阈值,默认值是500。多用于程序的数据统计,对性能较调意义不大。

  • dalvik.vm.stack-trace-file: 本参数控制Dalvik虚拟机的堆栈记录调试文件。用于系统调试,一般用户对其调整无意义。

  • dalvik.vm.execution-mode: 本参数控制Dalvik虚拟机的程序执行机制。可填写的值有”int:portable”、”int:fast”和”int:jit”。 int:portable表示以兼容模式运行(脚本翻译模式),此模式下程序的兼容性最高,但其执行效率最低(程序优化度依赖于dalvik虚拟机版本)。官方默认此模式。 int:fast表示以快速自优化模式运行(脚本翻译和预优化混合),此模式下程序的兼容性很高,执行效率也比较高。因为此时dalvik虚拟机允许程序使用自己的预定义优化模式和代码(包括C/C++/汇编代码)。推荐使用。 int:jit表示以Just-In-Time模式运行(JIT模式),此模式下程序的兼容性最差,但程序一旦加载后其运行效率最高(与C/C++直接编 写的程序效率无异),因为在此模式下dalvik虚拟机会预先将Java程序翻译成针对机器平台的本地语言(Native),同时完全允许代码中的所有预 优化和代码,允许所有不安全的非托管代码,同时不严谨的程序如果运行在JIT模式可能会造成内存泄露。但要注意,很多Dalvik虚拟机并不支持此模式 (如官方2.2)。

  • dalvik.vm.dexopt-flags: 本参数控制Dalvik虚拟机的程序代码校验和优化。可填写的值有m、v和o。 m为标准选项,可以是m=y或m=n。若m=y则启用不安全代码的校验和托管代码的优化。兼容性和安全性最高,推荐使用。 v为校验选项,可与o并存。可以是v=a或v=n。若v=a则表示校验所有代码,v=n则关闭代码的校验。 o为优化选项,可与v并存。可以是o=v或o=a。若o=v则表示优化以校验过的代码,o=a则表示优化所有代码。 例如: dalvik.vm.dexopt-flags=m=y dalvik.vm.dexopt-flags=v=n,o=v 注意,这个参数只会影响到安装APK之后或初次使用APK时生成dex文件时有效。若整个系统(包括应用程序)为odex化,则无意义。

  • dalvik.vm.verify-bytecode: 本参数控制Dalvik虚拟机是否验证应用程序的可执行代码。可以与上一个参数配合使用。可填写的值为true和false。 其具体意义与dalvik.vm.dexopt-flags的v=n一模一样。但可以与dalvik.vm.dexopt-flags配合使用以取得更好的效果。 例如: dalvik.vm.dexopt-flags=v=n,o=v dalvik.vm.verify-bytecode=false 这样可以令后来安装的apk文件可以被优化而不被检验。

  • dalvik.vm.checkjni: 本参数控制Dalvik虚拟机在调用外部jni链接库的时候是否对其做安全性检验。可填写的值为true和false。 此参数会覆盖ro.kernel.android.checkjni。 若值为true,会增加程序的兼容性和稳定性,但也会增加其加载和执行的时间。 推荐为false。 dalvik.vm.deadlock-predict: 本参数控制Dalvik虚拟机对程序死锁预测处理。可填写的值有off、warn和err。 off表示关闭死锁预测功能(默认设置)。 warn表示在继续程序运行的同时只记录该死锁预测(如果为真死锁就会出现程序假死现象,然后等N久出现关闭)。 err表示预测到死锁时马上弹出FC。 注意:有些Dalvik虚拟机版本并不支持此参数。

所以,如果设定:

dalvik.vm.heapstartsize=8m

dalvik.vm.heapgrowthlimit=64m

dalvik.vm.heapsize=256m

所以,每开启一个App,就会划出8m空间给它。如果使用过程超出8m,就会再次增加8m。但是只能增加7次空间,因为最大的空间为64m。超过了就会产生OOM。

由heapsize=256m,单个虚拟机分配的最大内存,相当于可以运行4个64m的应用。超过内存的部分就会进行内存回收,一方面是强制关闭一些应用,另一方面是加载新应用划分新的内存,这时候设备就会出现卡顿。

有些人喜欢用小widget,比如每个只占3-5m,如果按照8m去分配,每个小应用都会多浪费3m的内存。这种情况就可以把heapstartsize改小,可以增加可用内存。heapgrowthlimit倒不用改,因为现在应用使用内存整体都比较高。

内存分配流程

  1. 首先判断一下需要申请的size是不是过大,如果申请的size超过了堆的最大限制,则转入步骤6
  2. 尝试分配,如果成功则返回,失败则转入步骤3
  3. 判断是否gc正在进行垃圾回收,如果正在进行则等待回收完成之后,尝试分配。如果成功则返回,失败则转入步骤4
  4. 自己启动gc进行垃圾回收,这里gcForMalloc的参数是false。所以不会回收软引用,回收完成后尝试分配,如果成功则返回,失败则转入步骤5
  5. 调用dvmHeapSourceAllocAndGrow尝试分配,这个函数会扩张堆。所以heap startup的时候可以给一个比较小的初始堆,实在不够用再调用它进行扩张
  6. 进入回收软引用阶段,这里gcForMalloc的参数是ture,所以需要回收软引用。然后调用dvmHeapSourceAllocAndGrow尝试分配,如果失败则抛出OOM。

参数调整总结

dalvik.vm.heapstartsize  
堆分配的初始大小,调整这个值会影响到应用的流畅性和整体ram消耗。这个值越小,系统ram消耗越慢,但是由于初始值较小,一些较大的应用需要扩张这个堆,从而引发gc和堆调整的策略,会应用反应更慢。相反,这个值越大系统ram消耗越快,但是程序更流畅。

dalvik.vm.heapgrowthlimit       
极限堆大小,dvm heap是可增长的,但是正常情况下dvm heap的大小是不会超过dalvik.vm.heapgrowthlimit的值。如果受控的应用dvm heap size超过该值,则将引发oom。

dalvik.vm.heapsize
使用大堆时,极限堆大小。一旦dalvik heap size超过这个值,直接引发oom。在android开发中,如果要使用大堆,需要在manifest中指定android:largeHeap为true。这样dvm heap最大可达dalvik.vm.heapsize。

[dalvik.vm.heaptargetutilization]: [0.75]   可以设定内存利用率的百分比,当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。

dalvim GC策略是:

1.在一次GC后,根据当前Heap中已分配的内存大小除以dalvik.vm.heaputilization(0.75),得到一个目标值。

2.如果目标值不在(已分配的值+dalvik.vm.heapminfree)到(已分配的值+dalvik.vm.heapmaxfree)这个区间,即取区间边界值做为目标值(运行一段时间后第1步得到的目标值肯定会超过这个范围)。

3.虚拟机记录这个目标值,当做当前允许总的可以分配到的内存。同时根据目标值减去固定值(200~500K),当做触发GC_CONCURRENT事件的阈值。

4.当下一次分配内存,分配成功时。重新计算已分配的内存大小;若有达到GC_CONCURRENT的阈值,则产生GC。

5.当下一次分配内存,开始分配失败时。则会产生GC_FOR_ALLOC事件,释放内存;然后再尝试分配。

可以通过调整dalvik.vm.heapminfree 和dalvik.vm.heapmaxfree属性的值,减少GC_FOR_ALLOC和GC_CONCURRENT的次数,如果这两个值设置的过大,则会导致一次GC的时间过长,从而会看到明显的卡顿现象,设置的值既要使GC的次数减少,也不能是一次GC的时间过长。

在有的平台上,可以通过代码对单个应用的dalvik的属性进行设置,以减少对全局设置对系统的影响,可以再App里面通过如下的方式对当前的App的dalvik属性设置:

import dalvik.system.VMRuntime;  
import android.os.SystemProperties;  
...  
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);  
VMRuntime.getRuntime().setTargetHeapMinFree(2*1024*1024);  
VMRuntime.getRuntime().setTargetHeapConcurrentStart(8*1024*1024);  
...

如果想通过系统进行控制,也可以在framework里面的ActivityThread的handleBindApplication函数里面进行设置:

import dalvik.system.VMRuntime;  
import android.os.SystemProperties;  
import java.lang.*;  
...  
if( data.processName.equals("com.android.launcher")) {  
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);  
    VMRuntime.getRuntime().setTargetHeapMinFree(2*1024*1024);  
    VMRuntime.getRuntime().setTargetHeapConcurrentStart(8*1024*1024);  
}  
...

文章作者: Wossoneri
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 Wossoneri !
评论
  目录